#lang racket
; written by Petey Aldous III

(define first (make-parameter #t))

(define (lst->strs vals)
  (define (val->str val)
    (cond
      [(string? val) val]
      [(symbol? val) (symbol->string val)]
      [(number? val) (number->string val)]
      [else (error 'lst->strs "value of unknown type: ~e" val)]))
  (if (list? vals)
      (map val->str vals)
      (val->str vals)))

(define (trans-array-suffix array-suffix)
  (match array-suffix
    ['no-array "0"]
    [(? number?) (number->string array-suffix)]
    [else (error 'translate "index of unknown type: ~e" array-suffix)]
    #;[`(array ,indices ...)
       (error 'trans-array-suffix "Multi-dimensional arrays not supported")]
    #;[else (error 'trans-array-suffix "I don't recognize ~e" array-suffix)]))

(define (trans-expr exp)
  (match exp
    [(? number?) (format "new LValueInt(~a)" exp)]
    [(? symbol?) (format "new Variable(~s)" (symbol->string exp))]
    [`(array-lit ,exps ...) (string-append "new ArrayLitExpression(new Expression[] {"
                                           (string-join (map lst->strs
                                                             (map trans-expr exps))
                                                        ", ")
                                           "})")]
    [`(array-ref ,exps ...) (string-append "new ArrayRef(new Expression[] {"
                                           (string-join (map lst->strs
                                                             (map trans-expr exps))
                                                        ", ")
                                           "})")]
    [`(arith ,lhs (,ops ,rhss) ...)
     (foldl (λ (op rhs lhs)
              (format "new ArithExpr(~s, ~a, ~a)" op lhs rhs))
            (trans-expr lhs)
            ops
            (map trans-expr rhss))]
    [`(if ,cond-expr ,true-expr ,false-expr) (format "new TernaryExpr(~a, ~a, ~a)"
                                                     (trans-expr cond-expr)
                                                     (trans-expr true-expr)
                                                     (trans-expr false-expr))]
    [else (error 'trans-expr "I don't recognize ~e" exp)]))

(define (trans-init init)
  (match init
    ['no-init "UndefinedExpression.getInstance()"]
    [`(init ,value) (trans-expr value)]
    [else (error 'trans-init "I don't recognize ~e" init)]))

(define (get-new-id)
  (if (first)
      'first
      (gensym 'decl)))

(define (translate stmt)
  (match stmt
    [`(decl ,type ,name ,array-suffix ,init)
     (printf "\t\tStatement ~a = new DeclarationStatement(~s, ~s, ~a, ~a);~n"
             (get-new-id)
             (symbol->string type)
             (symbol->string name)
             (trans-array-suffix array-suffix)
             (trans-init init))]
    [`(= ,lvalue ,expr)
     (printf "\t\tStatement ~a = new AssignmentStatement(~a, ~a);~n"
             (gensym 'assign)
             (trans-expr lvalue)
             (trans-expr expr))]
    [`(cond-goto ,expr ,label)
     (printf "\t\tStatement ~a = new ConditionalGoto(~s, ~a);~n"
             (gensym 'cond)
             (symbol->string label)
             (trans-expr expr))]
    [`(goto ,label)
     (printf "\t\tStatement ~a = new Goto(~s);~n"
             (gensym 'goto)
             (symbol->string label))]
    [`(label ,name)
     (printf "\t\tlabels.put(~s, Statement.getLastNum());~n" (symbol->string name))]
    [else (error 'translate "I don't recognize ~e" stmt)]))

(printf 
 "import java.util.HashMap;
import controlGraph.*;

public class StatementFactory {
\tpublic static void main(String [] args) {
\t\t// This hash records each label on the first pass.
\t\tHashMap<String, Integer> labels = new HashMap<String, Integer>();
")
(let ([the-program (read)])
  (match the-program
    [`(program ,first-stmt ,stmts ...) (cons (translate first-stmt) (parameterize ([first #f])
                                                                      (map translate stmts)))]
    [else (error 'top-level "invalid program: ~e" the-program)])
  (void))
(printf "
\t\t// Make a fake successor statement
\t\tStatement.doneWithStatements();
\t\ttry {
\t\t\t// Make a second pass, this time through the AST.
\t\t\t// This gets line numbers from label names.
\t\t\tStatement.processLabels(labels);

\t\t\t// Begin the graph search
\t\t\tfirst.visitSuccessors(new store(), new ArrayArith());
\t\t} catch(InstantiationException ie) {

\t\t\t// If a label is referenced, but never defined, we have
\t\t\t// an invalid program. Print an error and exit.
\t\t\tSystem.err.println(\"Bad label: \" + ie.getMessage());
\t\t}
\t}
}
")
